/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __FREESTYLE_CHAINING_ITERATORS_H__
#define __FREESTYLE_CHAINING_ITERATORS_H__

/** \file
 * \ingroup freestyle
 * \brief Chaining iterators
 */

#include <iostream>

#include "Predicates1D.h"

#include "../system/Iterator.h"

#include "../view_map/ViewMap.h"
#include "../view_map/ViewMapIterators.h"
#include "../view_map/ViewMapAdvancedIterators.h"

// using namespace ViewEdgeInternal;

namespace Freestyle {

//
// Adjacency iterator used in the chaining process
//
///////////////////////////////////////////////////////////
class AdjacencyIterator : public Iterator {
 protected:
  ViewVertexInternal::orientedViewEdgeIterator _internalIterator;
  bool _restrictToSelection;
  bool _restrictToUnvisited;

 public:
  AdjacencyIterator()
  {
    _restrictToSelection = true;
    _restrictToUnvisited = true;
  }

  AdjacencyIterator(ViewVertex *iVertex,
                    bool iRestrictToSelection = true,
                    bool iRestrictToUnvisited = true)
  {
    _restrictToSelection = iRestrictToSelection;
    _restrictToUnvisited = iRestrictToUnvisited;
    _internalIterator = iVertex->edgesBegin();
    while ((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first))) {
      ++_internalIterator;
    }
  }

  AdjacencyIterator(const AdjacencyIterator &iBrother)
  {
    _internalIterator = iBrother._internalIterator;
    _restrictToSelection = iBrother._restrictToSelection;
    _restrictToUnvisited = iBrother._restrictToUnvisited;
  }

  AdjacencyIterator &operator=(const AdjacencyIterator &iBrother)
  {
    _internalIterator = iBrother._internalIterator;
    _restrictToSelection = iBrother._restrictToSelection;
    _restrictToUnvisited = iBrother._restrictToUnvisited;
    return *this;
  }

  virtual ~AdjacencyIterator()
  {
  }

  virtual string getExactTypeName() const
  {
    return "AdjacencyIterator";
  }

  virtual inline bool isEnd() const
  {
    return _internalIterator.isEnd();
  }

  virtual inline bool isBegin() const
  {
    return _internalIterator.isBegin();
  }

  /*! Returns true if the current ViewEdge is coming towards the iteration vertex.
   * False otherwise. */
  bool isIncoming() const;

  /*! Returns a *pointer* to the pointed ViewEdge. */
  virtual ViewEdge *operator*();

  virtual ViewEdge *operator->()
  {
    return operator*();
  }

  virtual AdjacencyIterator &operator++()
  {
    increment();
    return *this;
  }

  virtual AdjacencyIterator operator++(int)
  {
    AdjacencyIterator tmp(*this);
    increment();
    return tmp;
  }

  virtual int increment();

  virtual int decrement()
  {
    cerr << "Warning: method decrement() not implemented" << endl;
    return 0;
  }

 protected:
  bool isValid(ViewEdge *edge);
};

//
// Base class for Chaining Iterators
//
///////////////////////////////////////////////////////////

/*! Base class for chaining iterators.
 *  This class is designed to be overloaded in order to describe chaining rules.
 *  It makes the works of chaining rules description easier.
 *  The two main methods that need to overloaded are traverse() and init().
 *  traverse() tells which ViewEdge to follow, among the adjacent ones.
 *  If you specify restriction rules (such as "Chain only ViewEdges of the selection"),
 *  they will be included in the adjacency iterator.
 *  (i.e, the adjacent iterator will only stop on "valid" edges).
 */
class ChainingIterator : public ViewEdgeInternal::ViewEdgeIterator {
 protected:
  bool _restrictToSelection;
  bool _restrictToUnvisited;
  bool _increment;  // true if we're currently incrementing, false when decrementing

 public:
  ViewEdge *result;
  void *py_c_it;

  /*! Builds a Chaining Iterator from the first ViewEdge used for iteration and its orientation.
   *  \param iRestrictToSelection:
   *    Indicates whether to force the chaining to stay within
   *    the set of selected ViewEdges or not.
   *  \param iRestrictToUnvisited:
   *    Indicates whether a ViewEdge that has already been chained must be ignored ot not.
   *  \param begin:
   *    The ViewEdge from which to start the chain.
   *  \param orientation:
   *    The direction to follow to explore the graph. If true,
   *    the direction indicated by the first ViewEdge is used.
   */
  ChainingIterator(bool iRestrictToSelection = true,
                   bool iRestrictToUnvisited = true,
                   ViewEdge *begin = NULL,
                   bool orientation = true)
      : ViewEdgeIterator(begin, orientation)
  {
    _restrictToSelection = iRestrictToSelection;
    _restrictToUnvisited = iRestrictToUnvisited;
    _increment = true;
    py_c_it = NULL;
  }

  /*! Copy constructor */
  ChainingIterator(const ChainingIterator &brother) : ViewEdgeIterator(brother)
  {
    _restrictToSelection = brother._restrictToSelection;
    _restrictToUnvisited = brother._restrictToUnvisited;
    _increment = brother._increment;
    py_c_it = brother.py_c_it;
  }

  /*! Returns the string "ChainingIterator" */
  virtual string getExactTypeName() const
  {
    return "ChainingIterator";
  }

  /*! Inits the iterator context.
   *  This method is called each time a new chain is started.
   *  It can be used to reset some history information that you might want to keep.
   */
  virtual int init();

  /*! This method iterates over the potential next ViewEdges and returns the one that will be
   *  followed next. returns the next ViewEdge to follow or 0 when the end of the chain is reached.
   *  \param it:
   *    The iterator over the ViewEdges adjacent to the end vertex of the current ViewEdge.
   *    The Adjacency iterator reflects the restriction rules by only iterating over the valid
   *    ViewEdges.
   */
  virtual int traverse(const AdjacencyIterator &it);

  /* accessors */
  /*! Returns true if the orientation of the current ViewEdge corresponds to its natural
   * orientation */
  // inline bool getOrientation() const {}

  /*! Returns the vertex which is the next crossing */
  inline ViewVertex *getVertex()
  {
    if (_increment) {
      if (_orientation) {
        return _edge->B();
      }
      else {
        return _edge->A();
      }
    }
    else {
      if (_orientation) {
        return _edge->A();
      }
      else {
        return _edge->B();
      }
    }
  }

  /*! Returns true if the current iteration is an incrementation */
  inline bool isIncrementing() const
  {
    return _increment;
  }

  /* increments.*/
  virtual int increment();
  virtual int decrement();
};

//
// Chaining iterators definitions
//
///////////////////////////////////////////////////////////

/*! A ViewEdge Iterator used to follow ViewEdges the most naturally.
 *  For example, it will follow visible ViewEdges of same nature.
 *  As soon, as the nature or the visibility changes, the iteration stops (by setting the pointed
 *  ViewEdge to 0). In the case of an iteration over a set of ViewEdge that are both Silhouette
 *  and Crease, there will be a precedence of the silhouette over the crease criterion.
 */
class ChainSilhouetteIterator : public ChainingIterator {
 public:
  /*! Builds a ChainSilhouetteIterator from the first ViewEdge used for iteration and its
   *  orientation.
   *  \param iRestrictToSelection:
   *    Indicates whether to force the chaining to stay within the set of selected ViewEdges or
   *    not.
   *  \param begin:
   *    The ViewEdge from where to start the iteration.
   *  \param orientation:
   *    If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending
   *    ViewVertex of begin. If false, we'll search over the ViewEdges surrounding the ending
   *    ViewVertex of begin.
   */
  ChainSilhouetteIterator(bool iRestrictToSelection = true,
                          ViewEdge *begin = NULL,
                          bool orientation = true)
      : ChainingIterator(iRestrictToSelection, true, begin, orientation)
  {
  }

  /*! Copy constructor */
  ChainSilhouetteIterator(const ChainSilhouetteIterator &brother) : ChainingIterator(brother)
  {
  }

  /*! Returns the string "ChainSilhouetteIterator" */
  virtual string getExactTypeName() const
  {
    return "ChainSilhouetteIterator";
  }

  /*! This method iterates over the potential next ViewEdges and returns the one that will be
   *  followed next.
   *  When reaching the end of a chain, 0 is returned.
   */
  virtual int traverse(const AdjacencyIterator &it);

  /*! Inits the iterator context */
  virtual int init()
  {
    return 0;
  }
};

//
// ChainPredicateIterator
//
///////////////////////////////////////////////////////////

/*! A "generic" user-controlled ViewEdge iterator. This iterator is in particular built from a
 *  unary predicate and a binary predicate.
 *  First, the unary predicate is evaluated for all potential next ViewEdges in order to only
 *  keep the ones respecting a certain constraint.
 *  Then, the binary predicate is evaluated on the current ViewEdge together with each ViewEdge
 *  of the previous selection. The first ViewEdge respecting both the unary predicate and the
 *  binary predicate is kept as the next one. If none of the potential next ViewEdge respects
 *  these 2 predicates, 0 is returned.
 */
class ChainPredicateIterator : public ChainingIterator {
 protected:
  BinaryPredicate1D
      *_binary_predicate;              // the caller is responsible for the deletion of this object
  UnaryPredicate1D *_unary_predicate;  // the caller is responsible for the deletion of this object

 public:
  /*! Builds a ChainPredicateIterator from a starting ViewEdge and its orientation.
   *  \param iRestrictToSelection:
   *    Indicates whether to force the chaining to stay
   *    within the set of selected ViewEdges or not.
   *  \param iRestrictToUnvisited:
   *    Indicates whether a ViewEdge that has already been chained must be ignored ot not.
   *  \param begin:
   *    The ViewEdge from where to start the iteration.
   *  \param orientation:
   *    If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending
   *    ViewVertex of begin. If false, we'll search over the ViewEdges surrounding the ending
   *    ViewVertex of begin.
   */
  ChainPredicateIterator(bool iRestrictToSelection = true,
                         bool iRestrictToUnvisited = true,
                         ViewEdge *begin = NULL,
                         bool orientation = true)
      : ChainingIterator(iRestrictToSelection, iRestrictToUnvisited, begin, orientation)
  {
    _binary_predicate = 0;
    _unary_predicate = 0;
  }

  /*! Builds a ChainPredicateIterator from a unary predicate, a binary predicate, a starting
   *  ViewEdge and its orientation.
   *  \param iRestrictToSelection:
   *    Indicates whether to force the chaining to stay
   *    within the set of selected ViewEdges or not.
   *  \param iRestrictToUnvisited:
   *    Indicates whether a ViewEdge that has already been chained must be ignored ot not.
   *  \param upred:
   *    The unary predicate that the next ViewEdge must satisfy.
   *  \param bpred:
   *    The binary predicate that the next ViewEdge must satisfy together with the actual pointed
   *    ViewEdge.
   *  \param begin:
   *    The ViewEdge from where to start the iteration.
   *  \param orientation:
   *    If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending
   *    ViewVertex of begin. If false, we'll search over the ViewEdges surrounding the ending
   *    ViewVertex of begin.
   */
  ChainPredicateIterator(UnaryPredicate1D &upred,
                         BinaryPredicate1D &bpred,
                         bool iRestrictToSelection = true,
                         bool iRestrictToUnvisited = true,
                         ViewEdge *begin = NULL,
                         bool orientation = true)
      : ChainingIterator(iRestrictToSelection, iRestrictToUnvisited, begin, orientation)
  {
    _unary_predicate = &upred;
    _binary_predicate = &bpred;
  }

  /*! Copy constructor */
  ChainPredicateIterator(const ChainPredicateIterator &brother) : ChainingIterator(brother)
  {
    _unary_predicate = brother._unary_predicate;
    _binary_predicate = brother._binary_predicate;
  }

  /*! Destructor. */
  virtual ~ChainPredicateIterator()
  {
    _unary_predicate = 0;
    _binary_predicate = 0;
  }

  /*! Returns the string "ChainPredicateIterator" */
  virtual string getExactTypeName() const
  {
    return "ChainPredicateIterator";
  }

  /*! This method iterates over the potential next ViewEdges and returns the one that will be
   *  followed next. When reaching the end of a chain, 0 is returned.
   */
  virtual int traverse(const AdjacencyIterator &it);

  /*! Inits the iterator context */
  virtual int init()
  {
    return 0;
  }
};

} /* namespace Freestyle */

#endif  // __FREESTYLE_CHAINING_ITERATORS_H__
